package main

import(
	"fmt"
	"time"
	"github.com/samuel/go-zookeeper/zk"
	"sort"
	"strings"
	"os"
)

type DistributedLock struct{
	CurNode string 
	WaitNode string
	Conn *zk.Conn
	ServiceName string
}


func (l *DistributedLock) init() {

	hosts := []string{"127.0.0.1:2181"}; 
	conn, _, err := zk.Connect(hosts, time.Second*5);
	if(err != nil){
		fmt.Println("zk connect failed, caused by " + err.Error());
		return
	}
	l.Conn = conn

	exist, _, err := conn.Exists("/lock") 
	if err != nil { 
		fmt.Println("zk Exists failed, caused by " + err.Error());
		return 
	} 
	if(!exist){
		var acls = zk.WorldACL(zk.PermAll);
		_, err := conn.Create("/lock", []byte{}, 0, acls);
		if(err != nil){
			fmt.Println("zk create failed, caused by " + err.Error());
			return
		}
		fmt.Println( "/lock is create")
	}
}

func (l *DistributedLock) lock() {
	var acls = zk.WorldACL(zk.PermAll);
	name, err := l.Conn.Create("/lock/", []byte{}, 3, acls);
	if(err != nil){
		fmt.Println("zk create failed, caused by " + err.Error());
		return
	}
	l.CurNode = name
	fmt.Println(name, " create success")

	subnodes, _, _ := l.Conn.Children("/lock")
	sort.Strings(subnodes)
	fmt.Println("sort:", subnodes)
	if(subnodes[0] == strings.TrimLeft(name, "/lock/")){
		fmt.Println("1-get lock success")
		return
	}else{
		for idx, value := range subnodes{
			if(value == strings.TrimLeft(name, "/lock/")){
				l.WaitNode = "/lock/" +  subnodes[idx-1]
			}
		}
	}
	fmt.Println("waitNode: ", l.WaitNode)
	flag, _, event, _ := l.Conn.ExistsW(l.WaitNode)
	if(!flag){
		fmt.Println("2-get lock success")
		return		
	}else{
		select{
			case eventinfo := <-event:{
				if(eventinfo.Type == zk.EventNodeDeleted){
					fmt.Println("3-get lock success")
					return
				}
			}
		}
	}

}

func (l *DistributedLock) unlock() {
	fmt.Println("release lock")
	err := l.Conn.Delete(l.CurNode, -1) 
	if err != nil {
		fmt.Println("zk Delete failed, caused by " + err.Error());
		return 
	} 
	l.Conn.Close()
}

func main(){
	servicename := os.Args[1]
	for{
		lock := DistributedLock{}
		lock.ServiceName = servicename
		lock.init()
		lock.lock()
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), servicename, " get the lock")
		time.Sleep(time.Millisecond * 5000)
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), servicename, " release the lock")
		lock.unlock()		
	}
}